package com.code.springbootmybatismysql.configuration.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.code.springbootmybatismysql.configuration.datasource.DBEnum;
import com.code.springbootmybatismysql.configuration.datasource.DynamicDataSource;
import com.code.springbootmybatismysql.configuration.properties.DruidProperties;
import com.code.springbootmybatismysql.configuration.properties.MybatisConfigProperties;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**
 * 加载主从数据库连接信息(只加载连接信息装配好主从数据库连接信息)
 */
@Configuration
@EnableConfigurationProperties({
        MybatisConfigProperties.class,
        DruidProperties.class
})
public class DatabaseConfig {
    /**
     * Mapper xml path
     */
    private static final String MAPPER_PATH = "/mapper/**Mapper.xml";

    private static final String TYPE_ALIAS_PACKAGE = "com.code.springbootmybatismysql.model";

    @Autowired
    private MybatisConfigProperties mybatisConfigProperties;

    @Resource(name = "dataSourceMaster")
    private DataSource dataSourceMaster;

    @Resource(name = "dataSourceSlave1")
    private DataSource dataSourceSlave1;

    @Resource(name = "dataSourceLog")
    private DataSource dataSourceLog;

    @Resource(name = "dynamicDataSource")
    private DataSource dataSource;

    @Bean(name = "txManager")
    @Primary
    public DataSourceTransactionManager dataSourceTransactionManager() throws Exception {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(initDataSource());
        return dataSourceTransactionManager;
    }

    @Bean(name = "dataSourceMaster")
    public DruidDataSource initDataSourceMaster(DruidProperties druidProperties) throws SQLException {
        return initDruid(druidProperties, druidProperties.getMaster());
    }

    @Bean(name = "dataSourceSlave1")
    public DruidDataSource initDataSourceSlave1(DruidProperties druidProperties) throws SQLException {
        return initDruid(druidProperties, druidProperties.getSlave1());
    }

    @Bean(name = "dataSourceLog")
    public DruidDataSource initDataSourceLog(DruidProperties druidProperties) throws SQLException {
        return initDruid(druidProperties, druidProperties.getLog());
    }

    @Bean
    public SqlSessionFactoryBean create() throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        /** 设置mybatis configuration 扫描路径 */
        sqlSessionFactoryBean.setConfigLocation(new ClassPathResource(mybatisConfigProperties.getConfigLocation()));
        /** 添加mapper.xml 扫描路径 */
        PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + MAPPER_PATH;
        sqlSessionFactoryBean.setMapperLocations(pathMatchingResourcePatternResolver.getResources(packageSearchPath));

        /** 设置datasource */
        sqlSessionFactoryBean.setDataSource(dataSource);
        /** 设置typeAlias 包扫描路径 */
        sqlSessionFactoryBean.setTypeAliasesPackage(TYPE_ALIAS_PACKAGE);

        return sqlSessionFactoryBean;
    }

    @Bean(name = "dynamicDataSource")
    public AbstractRoutingDataSource initDataSource() {
        DynamicDataSource dataSource = new DynamicDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DBEnum.MASTER.getType(), dataSourceMaster);
        targetDataSources.put(DBEnum.SLAVE1.getType(), dataSourceSlave1);
        targetDataSources.put(DBEnum.LOG.getType(), dataSourceLog);

        dataSource.setDefaultTargetDataSource(dataSourceMaster);
        dataSource.setTargetDataSources(targetDataSources);

        return dataSource;
    }

    private DruidDataSource initDruid(DruidProperties props, DruidProperties.DbConfig config) throws SQLException {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(props.getDriverClassName());
        ds.setUrl(config.getUrl());
        ds.setUsername(config.getUsername());
        ds.setPassword(config.getPassword());
        ds.setTestWhileIdle(props.isTestWhileIdle());
        ds.setTestOnBorrow(props.isTestOnBorrow());
        ds.setTestOnReturn(props.isTestOnReturn());
        ds.setFilters(props.getFilters());

        ds.setInitialSize(config.getInitialSize());
        ds.setMinIdle(config.getMinIdle());
        ds.setMaxActive(config.getMaxActive());
        ds.setMaxWait(config.getMaxWait());
        ds.setTimeBetweenEvictionRunsMillis(config.getTimeBetweenEvictionRunsMillis());
        ds.setMinEvictableIdleTimeMillis(config.getMinEvictableIdleTimeMillis());
        ds.setMaxPoolPreparedStatementPerConnectionSize(config.getMaxPoolPreparedStatementPerConnectionSize());

        ds.init();
        return ds;
    }
}
